package com.tubeonfire.search.admin;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;

import com.google.appengine.api.search.Index;
import com.google.appengine.api.search.IndexSpec;
import com.google.appengine.api.search.ListRequest;
import com.google.appengine.api.search.Query;
import com.google.appengine.api.search.QueryOptions;
import com.google.appengine.api.search.Results;
import com.google.appengine.api.search.ScoredDocument;
import com.google.appengine.api.search.SearchServiceFactory;
import com.google.appengine.api.search.SortExpression;
import com.google.appengine.api.search.SortOptions;
import com.tubeonfire.entity.Tube;

public class TubeSearchModel {

	private static Index INDEX = SearchServiceFactory.getSearchService()
			.getIndex(IndexSpec.newBuilder().setName("tube"));
	private static final Logger log = Logger.getLogger(TubeSearchModel.class
			.getName());

	private int limit = 12;

	private int page = 1;

	private int totalResult = 0;

	private int totalReturn = 0;

	private int totalPage = 1;

	private static Cache cache = null;

	private static String cachePrefix = "backEndTubeSearch_";

	private static boolean isRegisted = false;

	private Tube currentItem = new Tube();

	private List<Tube> listResult = new ArrayList<Tube>();

	public int getTotalPage() {
		totalPage = totalResult / limit;
		if ((totalResult % limit) > 0) {
			totalPage += 1;
		}
		return totalPage;
	}

	public int getLimit() {
		return limit;
	}

	public void setLimit(int limit) {
		this.limit = limit;
	}

	public Tube getCurrentItem() {
		return currentItem;
	}

	public void setCurrentItem(Tube currentItem) {
		this.currentItem = currentItem;
	}

	public boolean isHasNextPage() {
		if (totalResult != 0) {
			return totalResult > (page * limit);
		} else {
			return false;
		}
	}

	public boolean isHasPreviousPage() {
		return page > 1;
	}

	public int getTotalResult() {
		return totalResult;
	}

	public int getTotalReturn() {
		return totalReturn;
	}

	public int getPage() {
		return page;
	}

	public void setPage(int page) {
		this.page = page;
	}

	public List<Tube> getListResult() {
		return listResult;
	}

	public void setListResult(List<Tube> listResult) {
		this.listResult = listResult;
	}

	public static void initCache() {
		if (!isRegisted) {
			isRegisted = true;
			try {
				cache = CacheManager.getInstance().getCacheFactory()
						.createCache(Collections.emptyMap());
			} catch (CacheException e) {
				log.warning(e.toString());
				e.printStackTrace();
				isRegisted = false;
			}
		}
	}

	@SuppressWarnings("unchecked")
	public static Tube getById(String id) {
		try {
			initCache();
			String prefix = cachePrefix + "id_" + id;
			Tube obj = new Tube();
			if (cache != null && cache.containsKey(prefix)) {
				obj = (Tube) cache.get(prefix);
			} else {
				QueryOptions options = QueryOptions.newBuilder().setLimit(1)
						.build();
				Query query = Query.newBuilder().setOptions(options)
						.build("id:\"" + id + "\"");
				Results<ScoredDocument> docResult = INDEX.search(query);
				if (docResult.getNumberFound() > 0) {
					for (ScoredDocument scoredDocument : docResult) {
						obj = TubeSearchEngine
								.documentToObjectByReflection(scoredDocument);
					}
				}
				if (obj != null && obj.getId().length() > 0) {
					cache.put(prefix, obj);
				} else {
					obj = null;
				}
			}
			return obj;
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			return null;
		}
	}

	public void prepareList() {
		try {
			listResult = new ArrayList<Tube>();
			Query query = Query
					.newBuilder()
					.setOptions(
							QueryOptions
									.newBuilder()
									.setLimit(limit)
									.setOffset(limit * (page - 1))
									.setFieldsToReturn("id", "title",
											"playlistId", "imageUrl",
											"totalTime", "view").build())
					.build("");

			Results<ScoredDocument> docResult = INDEX.search(query);
			totalReturn = (int) docResult.getNumberReturned();
			countTotal();
			if (docResult.getNumberFound() > 0) {
				for (ScoredDocument scoredDocument : docResult) {
					listResult.add(TubeSearchEngine
							.documentToObjectByReflection(scoredDocument));
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	public void prepareBanner() {
		try {
			StringBuilder queryString = new StringBuilder();
			queryString.append("status:" + 2);
			listResult = new ArrayList<Tube>();
			Query query = Query
					.newBuilder()
					.setOptions(
							QueryOptions
									.newBuilder()
									.setLimit(limit)
									.setOffset(limit * (page - 1))
									.setFieldsToReturn("id", "title",
											"playlistId", "imageUrl",
											"totalTime", "view").build())
					.build(queryString.toString());
			Results<ScoredDocument> docResult = INDEX.search(query);
			totalResult = (int) docResult.getNumberReturned();
			totalReturn = (int) docResult.getNumberReturned();
			if (docResult.getNumberFound() > 0) {
				Tube obj = new Tube();
				for (ScoredDocument scoredDocument : docResult) {
					obj = TubeSearchEngine
							.documentToObjectByReflection(scoredDocument);
					listResult.add(obj);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	public void searchById(String id) {
		try {
			listResult = new ArrayList<Tube>();
			Query query = Query.newBuilder().build("id:\"" + id + "\"");
			Results<ScoredDocument> docResult = INDEX.search(query);
			if (docResult.getNumberFound() > 0) {
				for (ScoredDocument scoredDocument : docResult) {
					currentItem = TubeSearchEngine
							.documentToObjectByReflection(scoredDocument);
				}
				listResult.add(currentItem);
			}
			totalReturn = 1;
			totalResult = 1;
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			currentItem = null;
		}
	}

	public void search(String playlistId, String channelId, String keyword) {
		try {
			StringBuilder queryString = new StringBuilder();
			boolean check = false;
			if (keyword != null && !keyword.isEmpty()) {
				queryString.append(keyword);
				check = true;
			}
			if (playlistId != null && !playlistId.isEmpty()) {
				if (!check) {
					queryString.append("playlistId:" + playlistId);
					check = true;
				} else {
					queryString.append(" AND playlistId:" + playlistId);
				}
			}
			if (channelId != null && !channelId.isEmpty()) {
				if (!check) {
					queryString.append("channelId:" + channelId);
					check = true;
				} else {
					queryString.append(" AND channelId:" + channelId);
				}
			}
			listResult = new ArrayList<Tube>();
			QueryOptions options = QueryOptions.newBuilder()
					.setNumberFoundAccuracy(1000).build();
			Query query = Query.newBuilder().setOptions(options)
					.build(queryString.toString());
			Results<ScoredDocument> docResult = INDEX.search(query);
			totalResult = (int) docResult.getNumberFound();

			SortOptions sortOptions = SortOptions
					.newBuilder()
					.addSortExpression(
							SortExpression
									.newBuilder()
									.setExpression("bumpPoint")
									.setDirection(
											SortExpression.SortDirection.DESCENDING)
									.setDefaultValueNumeric(1)).build();

			options = QueryOptions
					.newBuilder()
					.setSortOptions(sortOptions)
					.setNumberFoundAccuracy(1000)
					.setLimit(limit)
					.setOffset(limit * (page - 1))
					.setFieldsToReturn("id", "alias", "title", "playlistId",
							"imageUrl", "totalTime", "view").build();

			query = Query.newBuilder().setOptions(options)
					.build(queryString.toString());
			docResult = INDEX.search(query);
			totalReturn = (int) docResult.getNumberReturned();
			if (docResult.getNumberFound() > 0) {
				Tube obj = new Tube();
				for (ScoredDocument scoredDocument : docResult) {
					obj = TubeSearchEngine
							.documentToObjectByReflection(scoredDocument);
					listResult.add(obj);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	public void countTotal() {
		ListRequest request = ListRequest.newBuilder()
				.setReturningIdsOnly(true).build();
		totalResult = (int) INDEX.listDocuments(request).getResults().size();
	}

}
